package com.example.fuluchou.service.impl;

import com.example.fuluchou.common.errorcode.ServiceErrorCodeConstants;
import com.example.fuluchou.common.exception.ServiceException;
import com.example.fuluchou.common.untils.JacksonUtil;
import com.example.fuluchou.common.untils.RedisUtil;
import com.example.fuluchou.controller.param.CreateActivityParam;
import com.example.fuluchou.controller.param.CreatePrizeByActivityParam;
import com.example.fuluchou.controller.param.CreateUserByActivityParam;
import com.example.fuluchou.controller.param.PageParam;
import com.example.fuluchou.dao.dataobject.*;
import com.example.fuluchou.dao.mapper.*;
import com.example.fuluchou.service.ActivityService;
import com.example.fuluchou.service.dto.*;
import com.example.fuluchou.service.enums.ActivityPrizeStatusEnum;
import com.example.fuluchou.service.enums.ActivityPrizeTiersEnum;
import com.example.fuluchou.service.enums.ActivityStatusEnum;
import com.example.fuluchou.service.enums.ActivityUserStatusEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@Transactional(rollbackFor = Exception.class)
public class ActivityServiceImpl implements ActivityService {

    private static final String ACTIVITY_PREFIX = "ACTIVITY_";

    private static final Logger logger = LoggerFactory.getLogger(ActivityServiceImpl.class);

    private static final Long TIME = 60 * 60 * 24 * 3L;
    @Autowired
    UserMapper userMapper;

    @Autowired
    PrizeMapper prizeMapper;

    @Autowired
    ActivityMapper activityMapper;

    @Autowired
    ActivityPrizeMapper activityPrizeMapper;

    @Autowired
    ActivityUserMapper activityUserMapper;

    @Autowired
    RedisUtil redisUtil;

    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId){
        if(null == activityId){
            logger.warn("查询活动详细信息失败,activityId为空");
            return null;
        }
        //从Redis中获取信息
        ActivityDetailDTO aDDTO = getActivityDetailDTO(activityId);
        if(null != aDDTO){
            logger.info("查询活动详细信息成功,activityDetailDTO={}",
                    JacksonUtil.writeValueAsString(aDDTO));
            return aDDTO;
        }
        //redis中不存在，查表
        //活动表
        ActivityDO aDO = activityMapper.selectByActivityId(activityId);
        //活动奖品表
        List<ActivityPrizeDO> apDO = activityPrizeMapper.selectByActivityId(activityId);
        //活动人员表
        List<ActivityUserDO> auDO = activityUserMapper.selectByActivityId(activityId);
        //奖品表:先获取奖品ID
        List<Long> prizeIds = apDO.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDO = prizeMapper.batchSelectByIds(prizeIds);
        //整合信息
        ActivityDetailDTO activityDetailDTO = converToActivityDetailDTO(aDO,apDO,auDO,pDO);
        //redis存储
        cacheActivity(activityDetailDTO);
        //返回
        return activityDetailDTO;
    }

    @Override
    public void cacheActivity(Long activityId) {
        if(null == activityId) {
            logger.warn("要缓存的活动Id为空！");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITYID_IS_EMPTY);
        }
        //活动表
        ActivityDO aDO = activityMapper.selectByActivityId(activityId);
        if(null == aDO) {
            logger.warn("要查询的活动Id有误!");
            throw new ServiceException(ServiceErrorCodeConstants.CACHE_ACTIVITYID_ERROR);
        }
        //活动奖品表
        List<ActivityPrizeDO> apDO = activityPrizeMapper.selectByActivityId(activityId);

        //活动人员表
        List<ActivityUserDO> auDO = activityUserMapper.selectByActivityId(activityId);
        //奖品表:先获取奖品ID
        List<Long> prizeIds = apDO.stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> pDO = prizeMapper.batchSelectByIds(prizeIds);
        //整合信息 并 redis存储
        cacheActivity(converToActivityDetailDTO(aDO,apDO,auDO,pDO));

    }

    @Override
    public PageListDTO<ActivityDTO> findActivityList(PageParam param) {
        Long count = activityMapper.count();
        List<ActivityDO> activityDOList = activityMapper.queryActivityByPage(
                param.offset(), param.getPageSize());
        List<ActivityDTO> activityDTOList = activityDOList.stream()
                .map(activityDO -> {
                    ActivityDTO activityDTO = new ActivityDTO();
                    activityDTO.setActivityId(activityDO.getId());
                    activityDTO.setActivityName(activityDO.getActivityName());
                    activityDTO.setDescription(activityDO.getDescription());
                    activityDTO.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));
                    return activityDTO;
                })
                .collect(Collectors.toList());
        return new PageListDTO<>(count,activityDTOList);
    }

    @Override
    public CreateActivityDTO createActivity(CreateActivityParam request) {
        //校验活动信息
        checkActivytiInfo(request);

        //创建活动并且保存活动信息
        ActivityDO activityDO = new ActivityDO();
        activityDO.setActivityName(request.getActivityName());
        activityDO.setDescription(request.getDescription());
        activityDO.setStatus(ActivityStatusEnum.RUNNING.name());
        activityMapper.insert(activityDO);

        //保存活动关联奖品信息
        List<CreatePrizeByActivityParam> prizeByActivityParamList = request.getActivityPrizeList();
        List<ActivityPrizeDO> prizeParams = prizeByActivityParamList.stream()
                .map(createPrizeByActivityParam -> {
                    ActivityPrizeDO activityPrizeDO = new ActivityPrizeDO();
                    activityPrizeDO.setActivityId(activityDO.getId());
                    activityPrizeDO.setPrizeId(createPrizeByActivityParam.getPrizeId());
                    activityPrizeDO.setPrizeAmount(createPrizeByActivityParam.getPrizeAmount());
                    activityPrizeDO.setPrizeTiers(createPrizeByActivityParam.getPrizeTiers());
                    activityPrizeDO.setStatus(ActivityPrizeStatusEnum.INIT.name());
                    return activityPrizeDO;
                }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(prizeParams);

        //保存活动关联人员信息
        List<CreateUserByActivityParam> createUserByActivityParamList = request.getActivityUserList();
        List<ActivityUserDO> userParams = createUserByActivityParamList.stream()
                .map(createUserByActivityParam -> {
                    ActivityUserDO activityUserDO = new ActivityUserDO();
                    activityUserDO.setActivityId(activityDO.getId());
                    activityUserDO.setUserId(createUserByActivityParam.getUserId());
                    activityUserDO.setUserName(createUserByActivityParam.getUserName());
                    activityUserDO.setStatus(ActivityUserStatusEnum.INIT.name());
                    return activityUserDO;
                }).collect(Collectors.toList());
        activityUserMapper.batchInsert(userParams);

        //获取奖品基本属性列表
        //需要先获取需要的奖品ID
        List<Long> needPrizeIds = prizeParams.stream()
                .map(params -> params.getPrizeId())
                .distinct()
                .collect(Collectors.toList());
        List<PrizeDO> prizeBaseParams = prizeMapper.batchSelectByIds(needPrizeIds);

        //整合活动、人员、奖品完整信息
        ActivityDetailDTO detailDTO = converToActivityDetailDTO(activityDO, prizeParams, userParams, prizeBaseParams);

        //redis存储
        cacheActivity(detailDTO);

        //返回活动Id
        CreateActivityDTO createActivityDTO = new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    /**
     * 缓存完整的活动信息 ActivityDetailDTO
     *
     * @param detailDTO
     */
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        //key:ACTIVITY_12  VALUE:ActivityDetailDTO(json)
        if (null == detailDTO) {
            logger.warn("缓存的活动信息cacheActivity detailDTO:{} 不存在", detailDTO);
        }

        try {
            redisUtil.set(ACTIVITY_PREFIX + detailDTO.getActivityId(),
                    JacksonUtil.writeValueAsString(detailDTO),
                    TIME);
        } catch (Exception e) {
            logger.error("缓存活动信息异常,ActivityDetailDTO:{}",
                    JacksonUtil.writeValueAsString(detailDTO),
                    e);
        }

    }

    /**
     * 根据 activitId 从缓存中获取完整的活动信息
     *
     * @param activitId
     * @return
     */
    private ActivityDetailDTO getActivityDetailDTO(Long activitId) {
        if (null == activitId) {
            logger.warn("所要查找的活动Id不存在, key={}", activitId);
            return null;
        }
        try {
            String str = ACTIVITY_PREFIX + activitId;
            if (!StringUtils.hasText(str)) {
                logger.info("获取的缓存数据为空,key={}", activitId);
                return null;
            }
            ActivityDetailDTO activityDetailDTO = JacksonUtil.readValue(
                    redisUtil.get(str), ActivityDetailDTO.class);
            return activityDetailDTO;
        } catch (Exception e) {
            logger.error("从缓存中获取活动信息异常, key={}", activitId);
            return null;
        }

    }

    /**
     * 根据基本的DO整合完整信息
     * @param activityDO
     * @param prizeParams
     * @param userParams
     * @param prizeBaseParams
     * @return
     */
    private ActivityDetailDTO converToActivityDetailDTO(ActivityDO activityDO,
                                                        List<ActivityPrizeDO> prizeParams,
                                                        List<ActivityUserDO> userParams,
                                                        List<PrizeDO> prizeBaseParams) {
        ActivityDetailDTO result = new ActivityDetailDTO();
        result.setActivityId(activityDO.getId());
        result.setActivityName(activityDO.getActivityName());
        result.setDescription(activityDO.getDescription());
        result.setStatus(ActivityStatusEnum.forName(activityDO.getStatus()));

        List<ActivityDetailDTO.PrizeDTO> prizeDOList = prizeParams
                .stream()
                .map(prizeParam -> {
                    ActivityDetailDTO.PrizeDTO prizeDTO = new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setPrizeId(prizeParam.getPrizeId());
                    prizeDTO.setAmount(prizeParam.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeStatusEnum.forName(prizeParam.getStatus()));
                    prizeDTO.setTiers(ActivityPrizeTiersEnum.forName(prizeParam.getPrizeTiers()));

                    //需要从奖金基本信息表中筛选出与活动奖品ID相同的奖品，以便获取奖品完整信息
                    //Optional 是一个存放类型的容器 ，防止空指针产生的，java8中提供的
                    //filter 拦截非活动奖品ID的奖品信息
                    //findFirst 在奖品ID重复的情况下，只获取到第一个奖品ID相同的，
                    Optional<PrizeDO> optionalPrizeDO =
                            prizeBaseParams.stream()
                                    .filter(prizeBaseParam -> prizeBaseParam.getId().equals(prizeParam.getPrizeId()))
                                    .findFirst();
                    //如果 optionalPrizeDO 为空时，ifPresent 不会将其传入到 lambda 表达式中
                    //即 optionalPrizeDO 不为空时，才执行
                    optionalPrizeDO.ifPresent(prizeDO -> {
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setDescription(prizeDO.getDescription());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setImageurl(prizeDO.getImageurl());
                    });

                    return prizeDTO;
                }).collect(Collectors.toList());


        result.setPrizeDOList(prizeDOList);
        List<ActivityDetailDTO.UserDTO> userDOList = userParams.stream()
                .map(userParam -> {
                    ActivityDetailDTO.UserDTO userDTO = new ActivityDetailDTO.UserDTO();
                    userDTO.setUserId(userParam.getUserId());
                    userDTO.setUserName(userParam.getUserName());
                    userDTO.setStatus(ActivityUserStatusEnum.forName(userParam.getStatus()));
                    return userDTO;
                })
                .collect(Collectors.toList());
        result.setUserDOList(userDOList);

        return result;
    }

    private void checkActivytiInfo(CreateActivityParam param) {
        if (null == param) {
            throw new ServiceException(ServiceErrorCodeConstants.CREATE_ACTIVITY_INFO_IS_EMPTY);
        }

        //校验人员 Id 是否在 数据库中存在
        // param:1 2 3  but currenparam: 1 2
        List<Long> userIds = param.getActivityUserList()
                .stream()
                .map(CreateUserByActivityParam::getUserId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existUserIds = userMapper.selectExistByIds(userIds);
        if (CollectionUtils.isEmpty(existUserIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
        }
        userIds.forEach(id -> {
            if (!existUserIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_ERROR);
            }
        });

        // 奖品id在奖品表中是否存在
        List<Long> prizeIds = param.getActivityPrizeList()
                .stream()
                .map(CreatePrizeByActivityParam::getPrizeId)
                .distinct()
                .collect(Collectors.toList());
        List<Long> existPrizeIds = prizeMapper.selectExistByids(prizeIds);
        if (CollectionUtils.isEmpty(existPrizeIds)) {
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
        }
        prizeIds.forEach(id -> {
            if (!existPrizeIds.contains(id)) {
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_ERROR);
            }
        });

        //校验人员数是否大于奖品数(true:user>=prize)
        int userCount = existUserIds.size();
        long prizeCount = param.getActivityPrizeList()
                .stream()
                .mapToLong(CreatePrizeByActivityParam::getPrizeAmount)
                .sum();
        if (userCount < prizeCount) {
            throw new ServiceException(ServiceErrorCodeConstants.PRIZE_USER_ERROR);
        }

        //校验奖品等级是否被管理
        param.getActivityPrizeList().forEach(createPrizeByActivityParam -> {
            if (null == ActivityPrizeTiersEnum.forName(createPrizeByActivityParam.getPrizeTiers())) {
                throw new ServiceException(ServiceErrorCodeConstants.PRIZE_TIERS_ERROR);
            }
            ;
        });

    }
}
